package com.yr.visa.httpclient;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.yr.visa.util.IdFactory;

/**
 * 
 * @author fanrui
 *
 */
public class HttpsAction {

	private Logger logger = LoggerFactory.getLogger(getClass());

	private final Map<String, CloseableHttpClient> httpClients = new HashMap<String, CloseableHttpClient>();

	private ElideHttpClientGenerator httpClientGenerator = new ElideHttpClientGenerator();

	private static HttpsAction action = new HttpsAction();
	private String htmlCharset = "UTF-8";

	private HttpsAction() {
	}

	public static HttpsAction getInstance() {
		return action;
	}

	private CloseableHttpClient getHttpClient(Site site) {
		if (site == null) {
			return httpClientGenerator.getClient(null);
		}
		String domain = site.getDomain();
		CloseableHttpClient httpClient = httpClients.get(domain);
		if (httpClient == null) {
			synchronized (this) {
				httpClient = httpClients.get(domain);
				if (httpClient == null) {
					httpClient = httpClientGenerator.getClient(site);
					httpClients.put(domain, httpClient);
				}
			}
		}
		return httpClient;
	}
	public void cleanUp(Site site){
		if (site == null) {
			return ;
		}
		String domain = site.getDomain();
		CloseableHttpClient httpClient = httpClients.get(domain);
		if (httpClient != null) {
			synchronized (this) {
				httpClient = httpClients.get(domain);
				try {
					httpClient.close();
				} catch (IOException e) {
					logger.error("关闭domain失败",e);
				}
				httpClients.remove(domain);
			}
		}
		
	}
	public Response doRequest(Request request, Site site) {

		String charset = "UTF-8";
		Map<String, String> headers = request.getHeaders();
		logger.info(request.toString());
		CloseableHttpResponse httpResponse = null;
		int statusCode = 0;
		try {
			HttpUriRequest httpUriRequest = getHttpUriRequest(request, site,
					headers);
			httpResponse = getHttpClient(site).execute(httpUriRequest);
			statusCode = httpResponse.getStatusLine().getStatusCode();
			if (statusCode == 200) {
				Response page = handleResponse(request, charset, httpResponse);
				logger.info(page.getRawText());
				return page;
			} else {
				String _content = getContent(httpResponse);
				logger.warn("code error " + statusCode + "\t"
						+ request.getUrl()+",error:"+_content);
				Response response = new Response();
				response.setStatusCode(statusCode);
				if (statusCode == 302) {
					Header _header = httpResponse.getFirstHeader("Location");
					logger.warn("code error " + statusCode + "\t location"
							+ _header.getValue());
					response.setExtendsions(_header.getValue());
					response.setHttpResponse(httpResponse);
				}else if (statusCode == 500){
					response.setExtendsions(_content);
				}

				return response;
			}
		} catch (  Exception e) {
			logger.warn("doRequest page " + request.getUrl() + " error", e);
			Response response = new Response();
			response.setStatusCode(statusCode);
			response.setExtendsions("exception");
			return response;
		} finally {
			try {
				if (httpResponse != null) {
					// ensure the connection is released back to pool
					EntityUtils.consume(httpResponse.getEntity());
				}
			} catch (IOException e) {
				logger.warn("close response fail", e);
			}
		}
	}

	public Response doDownLoadFile(Request request, Site site) {

		Map<String, String> headers = request.getHeaders();

		logger.info(request.toString());
		CloseableHttpResponse httpResponse = null;
		int statusCode = 0;
		try {
			HttpUriRequest httpUriRequest = getHttpUriRequest(request, site,
					headers);
			httpResponse = getHttpClient(site).execute(httpUriRequest);
			statusCode = httpResponse.getStatusLine().getStatusCode();
			HttpEntity entity = httpResponse.getEntity();
			if (statusCode >= 300) {
				EntityUtils.consume(entity);
				logger.warn("code error " + statusCode + "\t"
						+ request.getUrl());
				throw null;
			} else {
				Response page = new Response();
				page.setUrl(request.getUrl());
				page.setRequest(request);
				page.setStatusCode(httpResponse.getStatusLine().getStatusCode());
				page.setHttpResponse(httpResponse);
				String fileName = null;
				Header[] dispositionHeaders = httpResponse
						.getHeaders("Content-disposition");
				if (dispositionHeaders != null && dispositionHeaders.length > 0) {
					fileName = extractFileName(dispositionHeaders[0].getValue());

				}
				if (fileName == null || "".equals(fileName.trim())) {
					fileName =getCustomerFileName(httpResponse);
				}
				InputStream inputStream = entity.getContent();
				File tempFile = new File(FileUtils.getTempDirectory(), fileName);
				logger.info("get file,{}",tempFile.getAbsolutePath());
				FileUtils.copyInputStreamToFile(inputStream, tempFile);
				page.setFile(tempFile);
				return page;

			}

		} catch (IOException e) {
			logger.warn("doRequest page " + request.getUrl() + " error", e);

			return null;
		} finally {
			try {
				if (httpResponse != null) {
					// ensure the connection is released back to pool
					EntityUtils.consume(httpResponse.getEntity());
				}
			} catch (IOException e) {
				logger.warn("close response fail", e);
			}
		}

	}

	private String getCustomerFileName(CloseableHttpResponse httpResponse ){
		String prefix= IdFactory.getDateTimeRandomStr().toString();
		Header ctHeader =httpResponse.getLastHeader("Content-Type");
		if(ctHeader != null){
			String _v =ctHeader.getValue();
			if(_v!= null){
				_v = _v.trim();
			}
			if( _v.contains(";")){
				_v = _v.substring(0,_v.indexOf(";"));
			}
			return prefix+ContentType.getSuffix(_v);
		}
		return prefix;
	}
	
	private String extractFileName(String headerValue) {
		String fileName = null;
		Pattern regex = Pattern.compile("(?<=filename=\").*?(?=\")");
		Matcher regexMatcher = regex.matcher(headerValue);
		if (regexMatcher.find()) {
			fileName = regexMatcher.group();
		}

		return fileName;
	}

	protected boolean statusAccept(Set<Integer> acceptStatCode, int statusCode) {
		return acceptStatCode.contains(statusCode);
	}

	protected HttpUriRequest getHttpUriRequest(Request request, Site site,
			Map<String, String> headers) {
		RequestBuilder requestBuilder = selectRequestMethod(request).setUri(
				request.getUrl());
		if (headers != null) {
			for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
				requestBuilder.addHeader(headerEntry.getKey(),
						headerEntry.getValue());
			}
		}
		RequestConfig.Builder requestConfigBuilder = RequestConfig.custom()
				.setConnectionRequestTimeout(request.getTimeout())
				.setSocketTimeout(request.getTimeout())
				.setConnectTimeout(request.getTimeout())
				.setRedirectsEnabled(false)
				.setCookieSpec(CookieSpecs.BEST_MATCH);
		requestConfigBuilder.setCookieSpec(CookieSpecs.IGNORE_COOKIES);
		requestBuilder.setConfig(requestConfigBuilder.build());
		
		return requestBuilder.build();
	}

	protected RequestBuilder selectRequestMethod(Request request) {
		String method = request.getMethod();
		if (method == null || method.equalsIgnoreCase(HttpConstant.Method.GET)) {
			// default get
			return RequestBuilder.get();
		} else if (method.equalsIgnoreCase(HttpConstant.Method.POST)) {
			RequestBuilder requestBuilder = RequestBuilder.post();
			requestBuilder.setEntity(request.getEntity());
			return requestBuilder;
		} else if (method.equalsIgnoreCase(HttpConstant.Method.HEAD)) {
			return RequestBuilder.head();
		} else if (method.equalsIgnoreCase(HttpConstant.Method.PUT)) {
			return RequestBuilder.put();
		} else if (method.equalsIgnoreCase(HttpConstant.Method.DELETE)) {
			return RequestBuilder.delete();
		} else if (method.equalsIgnoreCase(HttpConstant.Method.TRACE)) {
			return RequestBuilder.trace();
		}
		throw new IllegalArgumentException("Illegal HTTP Method " + method);
	}

	protected Response handleResponse(Request request, String charset,
			HttpResponse httpResponse) throws IOException {
		Response page = new Response();
		if(request.isFile()){
			page.setFile(getFileContent(httpResponse));
		}else{
			String content = getContent( httpResponse);
			page.setRawText(content);
		}
		page.setUrl(request.getUrl());
		page.setRequest(request);
		page.setStatusCode(httpResponse.getStatusLine().getStatusCode());
		page.setHttpResponse(httpResponse);
		return page;
	}

	protected String getContent(HttpResponse httpResponse) throws IOException {

		byte[] contentBytes = IOUtils.toByteArray(httpResponse.getEntity()
				.getContent());

		return new String(contentBytes, htmlCharset);

	}

	protected File getFileContent(HttpResponse httpResponse) throws UnsupportedOperationException, IOException {
		InputStream inputStream = httpResponse.getEntity().getContent();
		File tempFile = new File(FileUtils.getTempDirectory(),
				IdFactory.getUUIDSerialNumber());
		FileUtils.copyInputStreamToFile(inputStream, tempFile);

		return tempFile;
	}

}
